<?php
require_once('/usr/local/lib/php/oss/osslib.inc.php'); //外部直接引用该文件时候需要加载这个文件，如果通过base调用的话，这个不需要
//定义监控代码的使用模式：0-默认模式；1-CI框架；2-CGI方式
//不同的模式下，上报的url构造方法不同
defined('MONITOR_MODE_DEFAULT') || define('MONITOR_MODE_DEFAULT', 0);
defined('MONITOR_MODE_CI') || define('MONITOR_MODE_CI', 1);
defined('MONITOR_MODE_CGI') || define('MONITOR_MODE_CGI', 2);
defined('MONITOR_USE_MODE') || define('MONITOR_USE_MODE', MONITOR_MODE_CI);

/* 	
 * 	CI中的用法，在对应的controllers中load这个model即可，以下是使用的demo
 * 	CI：		$this->load->model('monitor_model');
 * 	$this->monitor_model->start('get_ticket'); //监控整个index函数
 * 	中间的函数省略掉
 * 	$this->monitor_model->stop('get_ticket', $login == 1 ? 0 : 100);
 * 	$this->monitor_model->send();
 * 
 */

/* 	
 * 	监控停止，同时构造出监控的数据
 * 	params: $index  监控的索引，与start中的相同，则构造data成功；不相同则不进行处理
 * 	params:	$status 监控数据的状态
 * 	$stauts	状态的描述（分三大类：0代表正常、>0代表警告和<0代表报警，报警的状态需要进行处理）
 * 	0：		程序正常
 * 	>0:		进行警告，属于可以接受范围内的异常（例如：活动过期，活动还没有开始）
 * 		1:	1开头的作为登陆状态的检测（100：代表登陆状态失效或者没有登陆）
 * 		2:	2开头作为活动的状态检测（例如：201-活动尚未开始，202-活动已结束，203-活动已下线，204-活动不存在等）
 * 		5:	5开头作为可以接受的数据异常处理
 * 	<0:		进行报警，属于严重的错误（例如：数据库执行出错，客户端传输到服务器的数据不符合预期等）
 * 	   -4:	-4开头的属于客户端传输过来的数据不符合预期（例如：应该传qq号码，但是却出来了'2123 or 1'字符串，这种情况很容导致sql注入）
 * 	   -5:	-5开头的属于系统出错（例如：数据库执行sql出错，数据异常等）
 * 	999：作为调试的状态，不需要进行处理
 */

class Monitor_model {

	const ip = "10.152.3.87";
	const port = 40000;

	private $url;
	private $domain;
	private $data;
	private $keys; //key对应启动的时间

	public function __construct() {
		$this->data = array();
		$this->keys = array();
		$this->_init_url();
		$d = explode(".", $_SERVER["SERVER_NAME"]);
		$this->domain = implode(".", array_slice($d, 0, count($d) - 2));
//		dump($_SERVER);
//		dump(debug_backtrace());
//		dump($this);
	}

	//初始化url，根据MONITOR_USE_MODE配置来确定不同的方法
	private function _init_url() {
		switch (MONITOR_USE_MODE) {
			case MONITOR_MODE_CI:   //CI模式
				$backtrace = debug_backtrace();
				$directory = $backtrace[4]['args'][0][0]->router->directory;
				$controller = $backtrace[3]['class'];
				$action = $backtrace[3]['function'];
				$path =  $directory. strtolower($controller). '/'. $action;
				$this->url = preg_replace('/'.str_replace('/', '\/', $path).'(.)*/', $path, $_SERVER["PHP_SELF"]);
				break;
			case MONITOR_MODE_CGI:	//CGI模式
				$this->url = $_SERVER['SCRIPT_NAME'];
				break;
			default:	//默认模式MONITOR_MODE_DEFAULT
				$this->url = $_SERVER["PHP_SELF"]; 
				break;
		}
	}

	//供外部修改上报的url
	public function set_url($url) {
		$this->url = $url;
	}

	public function program_start() {
		$this->_start('P');
	}

	public function program_stop($status = 0) {
		$this->_stop('P', $status);
	}

	public function unit_start($key) {
		if ($key == 'P') {
			return -1;
		}
		$this->_start($key);
	}

	public function unit_stop($key, $status) {
		if (!isset($this->keys[$key])) {
			return -1;
		}
		$this->_stop($key, $status);
	}

	private function _start($key) {
		$this->keys[$key] = microtime(true);
	}

	private function _stop($key, $status) {
		$this->data[] = array($key, $status, round((microtime(true) - $this->keys[$key]), 6) * 1000000); //数据精确到微妙
	}

	/*
	  $data:
	  (
	  ("apiname"=>"*","retcode"=>203,"exectime"=>600 ),
	  ("apiname"=>"query_jifen","retcode"=>203,"exectime"=>200 ),
	  ("apiname"=>"mg_gw","retcode"=>203,"exectime"=>200 ),
	  ("apiname"=>"idip","retcode"=>203,"exectime"=>200 ),
	  ...
	  )

	  return:
	  qt/phptest/kavenma/monitor/test_agent.php&R=100&P=5|240&api1=5|240
	 */

	public function to_string($data, $rate) {
		try {
			$array = array();
			$array[] = $this->domain . $this->url;
			dump($this->url);
			$array[] = 'R=' . $rate;
			foreach ($data as $l) {
				if (count($l) < 3) {
					continue;
				}
				$array[] = $l[0] . '=' . $l[1] . '|' . $l[2];
			}
			return implode('&', $array);
		} catch (Exception $e) {
			return '';
		}
	}

	public function send($rate = 100) {
		try {
			if (mt_rand() % 100 < $rate) {
				$str = $this->to_string($this->data, $rate);
				$this->load->osslib('UdpDgram');
				return $this->udpdgram->sendto($str, strlen($str), self::ip, self::port);
			}
			return 0;
		} catch (Exception $e) {
			return -1;
		}
	}

}

?>